/* Copyright (C) 2016-2017 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved
 * File:lktdecrypt.cpp
 * Author:guojianchuan
 * Date:2017-06-29
 */
/* 系统头文件 */
#include <stdio.h>
#include <string.h>
/* SQLRELAY 头文件 */
#include <sqlrelay/sqlrserver.h>
/* 本项目其他模块头文件 */

#include <iostream>
#include <functional>
#include <vector>

#include "lktcommon.h"
#include "lkterr.h"
#include "lktstruct.h"
#include "lktcipher.h"
#include "lktshm.h"
#include "zlog.h"


/* 当前模块头文件 */
class SQLRSERVER_DLLSPEC sqlrresultsettranslation_lktdecrypt :
                    public sqlrresultsettranslation {
    public:
            sqlrresultsettranslation_lktdecrypt(
                    sqlrservercontroller *cont,
                    sqlrresultsettranslations *rs,
                    xmldomnode *parameters);
            ~sqlrresultsettranslation_lktdecrypt();
        bool    run(sqlrserverconnection *sqlrcon,
                sqlrservercursor *sqlrcur,
                    const char *fieldname,
                    uint32_t fieldindex,
                    const char **field,
                    uint64_t *fieldlength);
    private:
            typedef std::function<int (std::string&, stringbuffer&)> 
                    Decryptor;

            Decryptor getDecryptor(const std::string& input);

            int decrypt (
                const std::string& input, 
                stringbuffer& output,
                const char* key,
                const char* iv);
            int decrypt_searchable (
                const std::string& input, 
                stringbuffer& output,
                const char* key, 
                const char* iv);
    private:

        stringbuffer output;        
        
        lktshm share;
        
        sqlrservercontroller *mysqlrcont;
};
sqlrresultsettranslation_lktdecrypt::
    sqlrresultsettranslation_lktdecrypt(
                sqlrservercontroller *cont,
                sqlrresultsettranslations *rs,
                xmldomnode *parameters) :
                sqlrresultsettranslation(cont,rs,parameters) {
                mysqlrcont = cont;
}
sqlrresultsettranslation_lktdecrypt::
    ~sqlrresultsettranslation_lktdecrypt() {
}

sqlrresultsettranslation_lktdecrypt::Decryptor 
  sqlrresultsettranslation_lktdecrypt::getDecryptor(const std::string& input)
{
    std::string::size_type n = input.find('|');

    if (n == std::string::npos) {
        return std::bind(
            &sqlrresultsettranslation_lktdecrypt::decrypt, 
            this, 
            std::placeholders::_1,
            std::placeholders::_2,
            share.getdatakey(),
            share.getdataiv());
    } else {
        return std::bind(
            &sqlrresultsettranslation_lktdecrypt::decrypt_searchable, 
            this, 
            std::placeholders::_1,
            std::placeholders::_2,
            share.getdatakey(),
            share.getdataiv());
    }
}

/**
* @Function:decrypt
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 加密字段数据
* @caution: 
*/
int sqlrresultsettranslation_lktdecrypt::decrypt (
    const std::string& input,
    stringbuffer& output,
    const char *datakey,
    const char *dataiv)
{
    int ret = ERR_COMMON_SUCCESS;
    /* BASE64密文转换为byte */
    int decodeLen = input.length();
    unsigned char *decodeText = new unsigned char [decodeLen];
    if (NULL == decodeText)
    {
        return ERR_COMMON_NO_MEM;
    }
    memset (decodeText, 0, decodeLen);
    
    int cipherTextLen = 0;
    if (NULL == LKT_DecodeBase64 ((unsigned char*) input.c_str(), decodeLen, decodeText, &cipherTextLen))
    {
        delete [] decodeText;
        return ERR_CONVERT_FAILED;
    }
    int plainTextLen = cipherTextLen + 1;
    unsigned char *tmpPlainText = new unsigned char[plainTextLen];
    if (NULL == tmpPlainText)
    {
        delete [] decodeText;
        return ERR_COMMON_NO_MEM;
    }
    memset (tmpPlainText, 0, plainTextLen);
    
    CIPHER_AES_S cipher;
    memset (&cipher, 0, sizeof (cipher));
    
    snprintf (cipher.algo, sizeof (cipher.algo), "%s", AES_256_CFB);
    cipher.mode     = DECRYPT;
    cipher.input    = decodeText;
    cipher.inlen    = cipherTextLen;
    cipher.key      = (unsigned char *)datakey;
    cipher.iv       = (unsigned char *)dataiv;
    cipher.output   = tmpPlainText;
    
    ret = LKT_AESCrypt(&cipher);
    if (ERR_COMMON_SUCCESS != ret)
    {
        delete [] decodeText;
        delete [] tmpPlainText;
        return ret;
    }
    output.clear ();
    output.write ((char*) tmpPlainText, cipher.outlen);
    
    delete [] decodeText;
    delete [] tmpPlainText;
    return ERR_COMMON_SUCCESS;
}


int sqlrresultsettranslation_lktdecrypt::decrypt_searchable (
    const std::string& input,
    stringbuffer& output,
    const char *datakey,
    const char *dataiv)
{
  std::vector<std::string> arr;

  std::string::size_type start = 0;
  std::string::size_type end = 0;
   while ((end = input.find('|', start)) != std::string::npos) {
    std::string substr = input.substr(start, end-start);
    start = end+1;
    arr.push_back(substr);
  }
  arr.push_back(input.substr(start, end-start));

  stringbuffer buffer;
  std::vector<std::string>::iterator it = arr.begin();
  for (;it != arr.end(); it++) {
    stringbuffer tmp;
    tmp.clear();
    int ret = decrypt(*it, tmp, datakey, dataiv);
    if (ret != ERR_COMMON_SUCCESS) {
        dzlog_error ("decrypt failed");
        return ret;
    }
    buffer.append(tmp.getString(), tmp.getSize());
  }
  std::swap(buffer, output);
  return ERR_COMMON_SUCCESS;
}

/**
* @Function:run
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 当前插件的入口函数
* @caution: 
*/
bool sqlrresultsettranslation_lktdecrypt::run(
                    sqlrserverconnection *sqlrcon,
                    sqlrservercursor *sqlrcur,
                    const char *fieldname,
                    uint32_t fieldindex,
                    const char **field,
                    uint64_t *fieldlength) {
    if ((NULL == fieldname) || (NULL == field) || (NULL == fieldlength))
    {
        return true;
    }
    if ((NULL == field[0]) || (0 == *fieldlength))
    {
        return true;
    }
    int ret = ERR_COMMON_SUCCESS;
    
    /* return true表示不再处理 */
    if (false == share.init ())
    {
        return true;
    }
    stringbuffer strfield;
    strfield.append (field[0], *fieldlength);
    const char* content = strfield.getString();
    uint64_t contentlen = strfield.getSize();
    
    if (1 == LKT_CheckStartIdentify(content))
    {
        dzlog_debug("field:%s, fieldlength:%lu", content, contentlen);
        
        /* 检查密文格式是否正确 */
        ret = LKT_CheckCipherFormat(content, share.getb64appdatakey());
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error("Invalid cipher format:%s", content);
            return true;
        }
        /* 解密策略的key和iv */
        ret = share.decodeDataKeyIv((unsigned char*) share.getb64appdatakey(), (unsigned char*) share.getb64appdataiv());
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error("decode key/iv failed");
            return true;
        }
        int len = strlen(content) + 1;
        char *cipherText = new char[len];
        if (NULL == cipherText)
        {
            dzlog_error ("no memory");
            return true;
        }
        memset (cipherText, 0, len);
        
        /* 获取真正的密文 */
        LKT_GetCipherText(content, cipherText);
        /* 解密密文 */

        std::string input(cipherText);
        delete [] cipherText;

        Decryptor decryptor = getDecryptor(input);

        output.clear();	
        ret = decryptor(input, output);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("decrypt failed, cipher text:%s", content);
            return true;
        }
        *field = output.getString();
        *fieldlength = output.getSize();
    }
    
    return true;
}
extern "C" {
    SQLRSERVER_DLLSPEC sqlrresultsettranslation
            *new_sqlrresultsettranslation_lktdecrypt(
                    sqlrservercontroller *cont,
                    sqlrresultsettranslations *rs,
                    xmldomnode *parameters) {
        return new sqlrresultsettranslation_lktdecrypt(
                            cont,rs,parameters);
    }
}
